/*
 * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.security.core.context;

import java.lang.reflect.Constructor;

import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

/**
 * SecurityContextHolder 用于储存安全上下文的信息
 * 当前的用户是谁，他是否已经被认证，他拥有哪些角色，都被保存在这里
 *
 *
 *
 * 将给定的{@link SecurityContext}与当前执行线程关联。
 *
 * <p>
 * 此类提供了一系列委托给的实例的静态方法
 * {@link org.springframework.security.core.context.SecurityContextHolderStrategy}. 这个类
 * 的目的是指定一种方便的方法来指定应该用于给定的 JVM
 * 这是一个JVM范围的设置，因为类中的所有内容都是 static 的，方便调用
 *
 * <p>
 * 若要指定应使用的策略，必须提供模式设置。
 * A 模式设置是定义为 static final 字段，或具体的完全限定类名实施
 * {@link org.springframework.security.core.context.SecurityContextHolderStrategy} 提供了无参的构造函数
 *
 * <p>
 * 有两种方法可以指定所需的策略模式
 * 1. 通过在 {@link #SYSTEM_PROPERTY} 上键入的系统属性来指定它。
 * 2. 在使用类之前调用 {@link #MODE_THREADLOCAL}
 * 如果这两种方法都没有使用，那么类将默认使用{@link#MODE_THREADLOCAL}，
 * 它是向后兼容的，具有较少的 JVM 不兼容性，并且适合于服务器（而 {@link #MODE_GLOBAL} 绝对不适合服务器使用）。
 *
 * @author Ben Alex
 *
 */
public class SecurityContextHolder {

	// 策略一：线程绑定（默认策略）
	public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
	// 策略二：本地可继承线程
	public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
	// 策略三：全局模式
	public static final String MODE_GLOBAL = "MODE_GLOBAL";

	// 保存策略名的属性
	public static final String SYSTEM_PROPERTY = "spring.security.strategy";
	// 策略名
	private static String strategyName = System.getProperty(SYSTEM_PROPERTY);

	// 安全上下文持有者策略接口
	private static SecurityContextHolderStrategy strategy;

	// 初始化数量
	private static int initializeCount = 0;

	static {
		initialize();
	}

	private static void initialize() {

		if (!StringUtils.hasText(strategyName)) {
			// 如果没有设置自定义的策略，则使用默认的策略
			strategyName = MODE_THREADLOCAL;
		}

		// 根据使用的策略创建策略对象
		// 使用 ThreadLocal 策略
		if (strategyName.equals(MODE_THREADLOCAL)) {
			strategy = new ThreadLocalSecurityContextHolderStrategy();
		}
		// 使用 InheritableThreadLocal 策略，它是 ThreadLocal 的一个子类
		else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
			strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
		}
		// 使用全局策略，实现方式就是static SecurityContext contextHolder
		else if (strategyName.equals(MODE_GLOBAL)) {
			strategy = new GlobalSecurityContextHolderStrategy();
		}
		else {
			// 如果不是上面的策略，尝试加载自定义策略
			try {
				Class<?> clazz = Class.forName(strategyName);
				Constructor<?> customStrategy = clazz.getConstructor();
				strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
			}
			catch (Exception ex) {
				ReflectionUtils.handleReflectionException(ex);
			}
		}
		initializeCount++;
	}

	/**
	 * 从当前线程中明确清除上下文值。
	 */
	public static void clearContext() {
		strategy.clearContext();
	}

	/**
	 * 获取当前 <code>SecurityContext</code>.
	 * @return SecurityContext
	 */
	public static SecurityContext getContext() {
		return strategy.getContext();
	}

	/**
	 * 主要用于故障排除，此方法显示类*重新初始化其*的次数。
	 * Primarily for troubleshooting purposes, this method shows how many times the class
	 * has re-initialized its <code>SecurityContextHolderStrategy</code>.
	 * @return the count (should be one unless you've called
	 * {@link #setStrategyName(String)} to switch to an alternate strategy.
	 *
	 * 计数（除非您已调用* {@link #setStrategyName（String）}切换到其他策略，否则应为1）。
	 */
	public static int getInitializeCount() {
		return initializeCount;
	}

	/**
	 * Associates a new <code>SecurityContext</code> with the current thread of execution.
	 * @param context the new <code>SecurityContext</code> (may not be <code>null</code>)
	 */
	public static void setContext(SecurityContext context) {
		strategy.setContext(context);
	}

	/**
	 * Changes the preferred strategy. Do <em>NOT</em> call this method more than once for
	 * a given JVM, as it will re-initialize the strategy and adversely affect any
	 * existing threads using the old strategy.
	 * @param strategyName the fully qualified class name of the strategy that should be
	 * used.
	 */
	public static void setStrategyName(String strategyName) {
		SecurityContextHolder.strategyName = strategyName;
		initialize();
	}

	/**
	 * 允许检索上下文策略. See SEC-1188.
	 * @return the configured strategy for storing the security context.
	 */
	public static SecurityContextHolderStrategy getContextHolderStrategy() {
		return strategy;
	}

	/**
	 * 将创建新的空上下文委派给已配置的策略。
	 */
	public static SecurityContext createEmptyContext() {
		return strategy.createEmptyContext();
	}

	@Override
	public String toString() {
		return "SecurityContextHolder[strategy='" + strategyName + "'; initializeCount=" + initializeCount + "]";
	}

}
